Asynchronous web security scanner with a FastAPI backend, React frontend, optional sqlmap integration, JSON reports, and SQLite history.
WebScanner is meant for quick, controlled audits of web applications. It can:
- crawl pages within the same domain,
- collect forms and basic metadata,
- scan common TCP ports,
- detect services and versions on open ports,
- enrich open ports with CVE matches from NVD and Vulnerability Lookup,
- score port risk from CVSS bands such as
9.8 -> Criticaland7.5 -> High, - inspect HTTP security headers,
- run reflected XSS heuristics,
- run lightweight SQLi differential checks,
- optionally launch deeper SQLi checks with
sqlmap, - save reports to disk and summaries to SQLite.
This is still a practical scanner for development and demo environments, not a replacement for a full penetration testing workflow.
The branch now includes:
- safer backend defaults for DB, CORS, cache TTL, and report paths,
- request throttling and optional API key protection,
- fixed
sqlmaptimeout handling and DB update after thesqlmapstage, - background
sqlmapjobs with polling instead of one long blocking HTTP request, - faster scan orchestration: ports, headers, crawl, XSS, and basic SQLi now do less serial waiting,
- a richer Ports tab with service detection, CVE mapping, and expandable risk details,
- better XSS and SQLi heuristics with fewer false positives,
- a new
GET /api/healthendpoint, - a backward-compatible
POST /api/scanendpoint, - frontend fixes for loading states, API base URL, result rendering, and keeping existing results visible during follow-up work,
- cleanup of generated DB/report files from git,
- a refreshed
README.mdand basic backend tests.
.
├─ backend/
│ ├─ app/
│ │ ├─ config.py
│ │ ├─ crawler.py
│ │ ├─ db.py
│ │ ├─ fetcher.py
│ │ ├─ headers_checker.py
│ │ ├─ main.py
│ │ ├─ models.py
│ │ ├─ port_scanner.py
│ │ ├─ reporter.py
│ │ ├─ sqli_tester.py
│ │ ├─ vuln_lookup.py
│ │ └─ xss_tester.py
│ ├─ data/
│ ├─ tests/
│ ├─ db_init.sql
│ ├─ Dockerfile
│ ├─ init_db.sh
│ └─ requirements.txt
├─ frontend/
│ ├─ src/
│ │ ├─ App.jsx
│ │ ├─ index.css
│ │ ├─ main.jsx
│ │ └─ components/
│ ├─ Dockerfile
│ ├─ nginx.conf
│ ├─ package.json
│ └─ vite.config.js
├─ img/
├─ sqlmap/
│ └─ Dockerfile
└─ docker-compose.yml
Important backend environment variables:
DATABASE_URLDefault: SQLite database inbackend/data/scans.dbOUTPUT_DIRLocal default:backend/data/reportsDocker compose default:/tmp/scan_reportsUSE_SQLMAPtrueorfalseSQLMAP_IMAGEDocker image used forsqlmapSQLMAP_CONTAINER_NAMEPrefix for transientsqlmapcontainersMAX_PAGES_LIMITMaximum crawl depth in pagesMAX_CONCURRENCYMaximum API-accepted scan concurrencyCORS_ALLOW_ORIGINSComma-separated allowed originsAPI_KEYOptional shared key expected inX-API-KeyRATE_LIMIT_WINDOW_SECONDSAPI rate-limit windowRATE_LIMIT_MAX_REQUESTSAPI requests allowed per window per clientSTATUS_POLL_RATE_LIMIT_MAX_REQUESTSSeparate per-window limit for background scan polling endpointsSCAN_CACHE_TTL_SECONDSHow long base scan results stay available for follow-upsqlmapSQLMAP_MAX_URLSHow many URLs from the crawl can be passed tosqlmapUse0to allow all crawler URLsENABLE_DOM_XSSEnables Playwright-based DOM confirmation checksSERVICE_DETECTION_ENABLEDEnables deeper service/version detection during the Ports scanENABLE_PORT_VULN_LOOKUPEnables CVE enrichment for detected servicesPORT_SCAN_TIMEOUT_SECONDSTimeout used for banner probing and fallback port checksPORT_VULN_MAX_RESULTSMaximum CVE matches stored per portPORT_VULN_REQUEST_TIMEOUT_SECONDSTimeout for external vulnerability API callsPORT_VULN_LOOKUP_CONCURRENCYConcurrency used for port vulnerability lookupsPORT_VULN_CACHE_TTL_SECONDSCache TTL for repeated service-to-CVE lookupsNVD_API_BASEDefault:https://services.nvd.nist.gov/rest/json/cves/2.0NVD_API_KEYOptional NVD API key. Strongly recommended if you expect multiple scans or many open services.VULNERABILITY_LOOKUP_API_BASEDefault:https://vulnerability.circl.lu/api
Frontend environment variables:
VITE_API_URLDefault:/apiVITE_API_KEYOptional, forwarded asX-API-Key
cd backend
python -m venv .venvWindows PowerShell:
.venv\Scripts\Activate.ps1
pip install -r requirements.txt
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000macOS/Linux:
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Generated data:
- SQLite DB:
backend/data/scans.db - Reports:
backend/data/reports/
cd frontend
npm install
npm run devBy default Vite runs on http://localhost:5173 and proxies /api to http://localhost:8000.
docker compose build
docker compose upServices:
- Frontend:
http://localhost:5173 - Backend API:
http://localhost:8000
In Docker mode, nginx serves the frontend and proxies /api to the backend container. Reports are written to ./scan_results through the mounted /tmp/scan_reports directory. The backend image now also installs nmap, so service detection on the Ports tab works inside Docker out of the box.
Health check:
{
"ok": true,
"sqlmap_enabled": true,
"cache_items": 0,
"base_scan_jobs": 0,
"sqlmap_jobs": 0
}Queues the base scan as a background job and returns immediately with a job object.
{
"url": "https://example.com",
"max_pages": 25,
"concurrency": 3,
"run_sqlmap": false
}Polls base scan status until it becomes completed or failed.
Queues a background sqlmap job using an existing scan_id.
{
"url": "https://example.com",
"scan_id": "previous-scan-id",
"run_sqlmap": true,
"sqlmap_args": ["--level=3", "--risk=2", "--threads=5", "--batch", "--random-agent"]
}The response returns immediately with a job object and parts.sqlmap.status set to queued or running.
Polls background sqlmap status until it becomes completed or failed.
Possible states:
queuedrunningcompletedfailed
Convenience endpoint that performs the base scan and, if requested, queues the follow-up sqlmap stage in the same request.
{
"url": "https://example.com",
"max_pages": 25,
"concurrency": 3,
"run_sqlmap": true,
"sqlmap_args": ["--level=3", "--risk=2", "--threads=5"]
}If API_KEY is configured on the backend, send:
X-API-Key: your-shared-key
{
"scan_id": "0eab4f5d-...",
"target": "https://example.com",
"report": "/tmp/scan_reports/report_https_example.com_20260324T120000Z.json",
"job": {
"job_id": "4b43f2b7-...",
"scan_id": "0eab4f5d-...",
"status": "running",
"error": null,
"created_at": 12345.0,
"updated_at": 12350.0,
"scanned_urls": ["https://example.com/login?id=1"]
},
"parts": {
"ports": {
"ok": true,
"source": "nmap",
"ports": {
"80": true,
"443": true,
"22": false
},
"summary": {
"open_port_count": 2,
"highest_cvss": 9.8,
"highest_severity": "Critical",
"ports_with_vulnerabilities": 1
},
"items": [
{
"port": 80,
"protocol": "tcp",
"state": "open",
"open": true,
"service": {
"name": "http",
"product": "nginx",
"version": "1.28.2",
"detection": "nmap-sV"
},
"risk": {
"cve_count": 2,
"highest_cvss": 9.8,
"severity": "Critical"
},
"vulnerabilities": [
{
"id": "CVE-2024-12345",
"sources": ["nvd", "vulnerability-lookup"],
"cvss": 9.8,
"severity": "Critical",
"summary": "Example summary",
"link": "https://nvd.nist.gov/vuln/detail/CVE-2024-12345"
}
]
}
]
},
"crawl": {
"urls": ["https://example.com", "https://example.com/login"],
"forms": [
[
"https://example.com/login",
{
"method": "post",
"inputs": {
"username": "",
"password": ""
},
"enctype": "application/x-www-form-urlencoded"
}
]
]
},
"headers": {
"present": {},
"missing": ["Content-Security-Policy"]
},
"xss": [],
"sqli": [],
"sqlmap": {
"status": "running",
"job_id": "4b43f2b7-...",
"ok": null,
"error": null,
"message": "sqlmap is running.",
"findings": [],
"scanned_urls": ["https://example.com/login?id=1"]
}
}
}Basic backend tests were added for:
- SQLMap argument sanitization,
- summary counting,
- SQLMap output parsing,
- nmap service parsing,
- CVSS-to-risk mapping.
Run them with:
cd backend
python -m pytest- The frontend now expects
/apiby default, which works both with Vite proxy and nginx proxy. - Large base scans no longer depend on one long-lived nginx request. The frontend starts the scan, then polls
GET /api/scan_no_sqlmap/{job_id}until the report is ready. - Base scan now arrives in stages:
ports + headers + crawlfirst, thenport CVE enrichment + XSS + SQLicontinue in the background and fill the open tabs progressively. - The Ports tab is now compact by default and expandable per port. Each row shows the port, detected service, highest risk band, and number of CVE matches; clicking the row opens full details.
- CVE mapping is heuristic because it is based on detected service fingerprints, not a guaranteed asset inventory. Treat it as enrichment for triage, not as a formal proof that a host is vulnerable.
- The scanner combines NVD and Vulnerability Lookup results and ranks each port by the highest matched CVSS score.
sqlmapnow accepts the full crawler URL set by default and prioritizes parameterized/form URLs first. Plain URLs fall back to lightweight crawl-assisted probing when needed.- The frontend keeps the already loaded report visible while a follow-up
sqlmapjob is queued or running; only thesqlmaptab changes state. - Long
sqlmapruns no longer rely on a single long-lived nginx request. The backend returns a job id immediately, and the frontend polls status updates. - If you want stricter protection, set
API_KEYin the backend andVITE_API_KEYin the frontend. sqlmapruns are intentionally capped and sanitized to keep the app safer and more predictable.









